package com.anghui.demo.aspectj;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.StrUtil;
import com.anghui.demo.annotations.Log;
import com.anghui.demo.domain.RmOperateLog;
import com.anghui.demo.service.OperateLogService;
import com.anghui.demo.utils.IpUtils;
import com.anghui.demo.utils.ServletUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.NamedThreadLocal;
import org.springframework.stereotype.Component;

import java.util.Objects;

@Aspect
@Component
public class LogAspect {
    @Autowired
    private Snowflake snowflake;
    @Autowired
    private OperateLogService operateLogService;
    private static final ThreadLocal<Long> TIME_THREADLOCAL = new NamedThreadLocal<>("Cost Time");

    @Pointcut("@annotation(log)")
    public void pointCut(Log log) {
    }

    @Around(value = "pointCut(log)", argNames = "proceedingJoinPoint,log")
    public Object around(ProceedingJoinPoint proceedingJoinPoint, Log log) throws Throwable {
        System.out.println("0.前置环绕通知");
        Object proceed = proceedingJoinPoint.proceed();
        System.out.println("4.后置环绕通知");
        return proceed;
    }

    @Before(value = "pointCut(log)", argNames = "joinPoint,log")
    public void before(JoinPoint joinPoint, Log log) {
        System.out.println("1.前置通知");
        TIME_THREADLOCAL.set(System.currentTimeMillis());
    }

    @After(value = "pointCut(log)", argNames = "joinPoint,log")
    public void after(JoinPoint joinPoint, Log log) {
        System.out.println("3.后置通知");
    }

    @AfterReturning(
            pointcut = "pointCut(log)",
            returning = "jsonResult",
            argNames = "joinPoint,log,jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Log log, Object jsonResult) {
        System.out.println("2.后置Returning通知");
        handleLog(joinPoint, log, jsonResult, null);
    }

    @AfterThrowing(
            pointcut = "pointCut(log)",
            throwing = "e",
            argNames = "joinPoint,log,e")
    public void doAfterThrowing(JoinPoint joinPoint, Log log, Exception e) {
        handleLog(joinPoint, log, null, e);
    }

    private void handleLog(JoinPoint joinPoint, Log controllerLog, Object jsonResult, Exception e) {
        try {
            Long createBy = 123456L;
            RmOperateLog rmOperateLog = new RmOperateLog();
            rmOperateLog.setId(snowflake.nextId());
            rmOperateLog.setOperateIp(IpUtils.getClientIp());
            rmOperateLog.setOperateLocation("内网");
            rmOperateLog.setCreateBy(createBy);
            rmOperateLog.setStatus(0);
            rmOperateLog.setOperateUrl(StrUtil.sub(Objects.requireNonNull(ServletUtils.getRequest()).getRequestURI(), 0, 255));
            if (e != null) {
                rmOperateLog.setStatus(1);
                rmOperateLog.setErrorMsg(StrUtil.sub(e.getMessage(), 0, 2000));
            }
            rmOperateLog.setTypeName(joinPoint.getSignature().getDeclaringTypeName());
            rmOperateLog.setMethodName(joinPoint.getSignature().getName());
            rmOperateLog.setBusinessType(controllerLog.businessType());
            rmOperateLog.setTitle(controllerLog.title());
            rmOperateLog.setRemark(controllerLog.remark());
            rmOperateLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get());
            operateLogService.getBaseMapper().insert(rmOperateLog);
        } catch (Exception e1) {
            e1.printStackTrace();
        } finally {
            TIME_THREADLOCAL.remove();
        }
    }
}
